from django.shortcuts import redirect, render, reverse, HttpResponse
from rbac import models
from rbac.service import urls
from collections import OrderedDict
from django.forms import formset_factory
from rbac.forms import MultiForms


def menu_list(request):
    menu_queryset = models.Menu.objects.all()
    current_mid = request.GET.get('mid')
    current_sid = request.GET.get('sid')
    if current_mid:
        second_menus = models.Permission.objects.filter(menu_id=current_mid)
    else:
        second_menus = []
    if current_sid:
        permissions_list = models.Permission.objects.filter(pid=current_sid)
    else:
        permissions_list = []
    return render(request, 'rbac/menu_list.html', {'menus': menu_queryset,
                                                   'current_mid': current_mid,
                                                   'second_menus': second_menus
        , 'current_sid': current_sid,
                                                   'permissions_list': permissions_list
                                                   }
                  )


def menu_add(request):
    if request.method == 'POST':
        title = request.POST.get('title', None)
        if title:
            models.Menu.objects.create(title=title)
            url = urls.memory_reverse(request, 'rbac:menu_list')
            return redirect(url)
        else:
            return render(request, 'rbac/menu_add.html', {'error': '该字段不能为空'})
    return render(request, 'rbac/menu_add.html')


def menu_edit(request, menu_id):
    menu_obj = models.Menu.objects.filter(pk=menu_id).first()
    title_list = [item['title'] for item in models.Menu.objects.all().values('title')]
    if request.method == 'POST':
        title = request.POST.get('title', None)
        if not title or title in title_list:
            return render(request, 'rbac/menu_edit.html', )
        else:
            models.Menu.objects.filter(pk=menu_id).update(title=title)
            url = urls.memory_reverse(request, 'rbac:menu_list')
            return redirect(url)
    return render(request, 'rbac/menu_edit.html', {'menu_obj': menu_obj})


def menu_del(request, menu_id):
    cancel_back_url = urls.memory_reverse(request, 'rbac:menu_list')
    if request.method == 'POST':
        models.Menu.objects.filter(pk=menu_id).delete()
        url = urls.memory_reverse(request, 'rbac:menu_list')
        return redirect(url)
    return render(request, 'rbac/del.html', {'cancel': cancel_back_url})


def second_menu_add(request, current_mid):
    """
    添加二级菜单
    :param request:
    :param current_mid:
    :return:
    """
    # 先获当前所在的一级菜单,给添加框设置一级菜单默认值
    menu_obj = models.Menu.objects.filter(pk=current_mid).first()
    sec_menu_obj = models.Permission.objects.filter(menu_id=current_mid).values('title')
    sec_name_list = [item['title'] for item in sec_menu_obj]
    if request.method == 'POST':
        title = request.POST.get('title', None)
        url = request.POST.get('url', None)
        alias = request.POST.get('alias', None)
        if title or title not in sec_name_list:
            models.Permission.objects.create(title=title, url=url, alias=alias, menu_id=current_mid)
            url_raw = urls.memory_reverse(request, 'rbac:menu_list', )
            return redirect(url_raw)
    return render(request, 'rbac/second_menu_add.html', locals())


def second_menu_del(request, sid):
    """
    删除二级菜单
    :param request:
    :param sid:
    :return:
    """
    cancel_back_url = urls.memory_reverse(request, 'rbac:menu_list')
    if request.method == 'POST':
        models.Permission.objects.filter(pk=sid).delete()
        url = urls.memory_reverse(request, 'rbac:menu_list')
        return redirect(url)
    return render(request, 'rbac/del.html', {'cancel': cancel_back_url})


def second_menu_edit(request, sid):
    """ 编辑二级菜单"""
    menu_obj = models.Menu.objects.all()
    second_menu_obj = models.Permission.objects.filter(pk=sid).first()
    if request.method == 'POST':
        title = request.POST.get('title')
        url = request.POST.get('url')
        alias = request.POST.get('alias')
        menu_id = request.POST.get('menu_id')
        models.Permission.objects.filter(pk=sid).update(title=title, url=url, alias=alias, menu_id=menu_id)
        url = urls.memory_reverse(request, 'rbac:menu_list')
        return redirect(url)
    return render(request, 'rbac/second_menu_edit.html', {'second_menus': second_menu_obj, 'menu_obj': menu_obj})


def permission_menu_add(request, sid):
    """二级菜单下的权限增加"""
    sec_menu_obj = models.Permission.objects.filter(pk=sid).first()
    if not sec_menu_obj:
        return HttpResponse('此二级菜单不存在')
    if request.method == 'POST':
        title = request.POST.get('title')
        url = request.POST.get('url')
        alias = request.POST.get('alias')
        models.Permission.objects.create(title=title, url=url, alias=alias, pid_id=sid)
        url = urls.memory_reverse(request, 'rbac:menu_list')
        return redirect(url)
    return render(request, 'rbac/permission_menu_add.html', {'sec_menu_obj': sec_menu_obj})


def permission_menu_del(request, permission_id):
    cancel = urls.memory_reverse(request, 'rbac:menu_list')
    if request.method == 'POST':
        models.Permission.objects.filter(pk=permission_id).delete()
        url = urls.memory_reverse(request, 'rbac:menu_list')
        return redirect(url)
    return render(request, 'rbac/del.html', {'cancel': cancel})


def multi_permissions(request):
    """
    批量操作权限
    :param request:
    :return:
    """
    action_type = request.GET.get('type')
    formset_add_class = formset_factory(MultiForms.MultiPermissionsForm, extra=0)
    update_formset_class = formset_factory(MultiForms.MultiUpdatePermissionsForm, extra=0)
    add_formset = None

    if request.method == 'POST' and action_type == 'add':
        # 为批量添加
        formset = formset_add_class(data=request.POST)
        if formset.is_valid():
            obj_list = []
            has_error = False
            form_data_list = formset.cleaned_data
            for i in range(0, formset.total_form_count()):
                form_data = form_data_list[i]
                try:
                    new_obj = models.Permission(**form_data)
                    new_obj.validate_unique()
                    # 如果验证成功。则把所有对象加入列表
                    obj_list.append(new_obj)
                except Exception as e:
                    formset.errors[i].update(e)
                    add_formset = formset
                    has_error = True
            if not has_error:
                # 没有错误，批量增加到数据库
                models.Permission.objects.bulk_create(obj_list, batch_size=100)  # 一次增加一百条记录
        else:
            add_formset = formset  # 将带有错误信息的formset传给模板显示
    update_formset = None

    if request.method == 'POST' and action_type == 'update':
        # 为批量更新
        formset = update_formset_class(data=request.POST)
        if formset.is_valid():
            form_data_list = formset.cleaned_data
            for i in range(0, formset.total_form_count()):
                form_data = form_data_list[i]
                permission_id = form_data.pop('id')
                try:
                    edit_obj = models.Permission.objects.filter(id=permission_id).first()
                    for k, v in form_data.items():
                        setattr(edit_obj, k, v)
                        # 映射 k，v 进行唯一校验
                    edit_obj.validate_unique()
                    edit_obj.save()
                except Exception as  e:
                    formset.errors[i].update(e)
                    update_formset = formset
        else:
            update_formset = formset

    from rbac.service.routers import get_all_url_dict
    # 1.获取项目中所有的url
    all_url_dict = get_all_url_dict()  # 调用获取url的函数
    """{
        'rbac:menu_list':{'alias:xxx',url:'xx/aa/bb'}
    }"""
    router_name_set = set(all_url_dict.keys())  # 给url所有的别名存放在一个集合中

    # 2. 获取数据库中所有的url
    permission = models.Permission.objects.all().values('id', 'title', 'url', 'alias', 'menu_id', 'pid_id')
    permission_dict = OrderedDict()  # 设置有序字典
    for item in permission:
        permission_dict[item['alias']] = item  # 将数据库中的别名作为字典的key，记录作为value
    permission_name_set = set(permission_dict.keys())  # 将数据库中的url别名存放一个集合中
    # 3.应该添加、删除、修改的 权限
    # 3.1 应该添加的列表： 自动发现的url集合有，数据库没有的
    if not add_formset:
        add_name_list = router_name_set - permission_name_set
        # 显示默认值
        add_formset = formset_add_class(
            initial=[item_dict for name, item_dict in all_url_dict.items() if name in add_name_list])

    # 3.2 代删除列表，数据库有，自动发现的没有
    delete_name_list = permission_name_set - router_name_set
    delete_item_list = [item_dict for alias, item_dict in permission_dict.items() if alias in delete_name_list]
    # 3.3 待更新列表，数据库和自动发现都有的部分
    for alias, value in permission_dict.items():
        if not all_url_dict.get('alias'):
            continue
        if value['url'] != all_url_dict.get('alias')['url']:
            value['url'] = '路由和数据库中的不一致'
    update_name_list = permission_name_set & router_name_set
    if not update_formset:
        update_formset = update_formset_class(initial=[
            item_dict for alias, item_dict in permission_dict.items() if alias in update_name_list
        ])
    return render(request, 'rbac/multi_permissions.html', {'add_formset': add_formset,
                                                           'del_item_list': delete_item_list,
                                                           'update_formset': update_formset})


def multi_permissions_del(request, permission_id):
    cancel_back_url = urls.memory_reverse(request, 'rbac:multi_permissions')
    if request.method == 'POST':
        models.Permission.objects.filter(pk=permission_id).delete()
        return redirect(cancel_back_url)
    return render(request, 'rbac/del.html', {'cancel': cancel_back_url})


def distribute_permissions(request):
    """权限分配"""

    # 点击用户、显示对应的角色、权限默认值
    user_id = request.GET.get('uid')
    user_obj = models.UserInfo.objects.filter(id=user_id).first()

    # 获取角色ID
    role_id = request.GET.get('rid')
    role_obj = models.Role.objects.filter(id=role_id).first()
    if not role_obj:
        role_id = None

    if not user_obj:
        user_id = None
    # 选中保存设置
    if request.method == 'POST' and request.POST.get('type') == 'role':
        # 为用户选择角色
        roles_id_list = request.POST.getlist('roles')
        # 添加到用户和角色多对多的表中
        if not user_obj:
            return HttpResponse('请先选择用户')
        user_obj.roles.set(roles_id_list)
    if request.method == 'POST' and request.POST.get('type') == 'permission':
        # 为角色选择权限
        permissions_id_list = request.POST.getlist('permissions')
        if not role_obj:
            return HttpResponse('请先选择角色')
        role_obj.permissions.set(permissions_id_list)

    # 根据 用户uid 获取该用户所拥有的角色的id
    if user_id:
        has_user_role = user_obj.roles.all()
    else:
        has_user_role = []
    # 根据role id 生成字典的key，传入模板进行判断
    has_user_role_dict = {item.id: None for item in has_user_role}

    """
    如果选中角色，优先显示选中角色所拥有的权限
    如果没有选中角色，才显示用户所有用的权限
    """
    if role_obj:
        has_user_permissions = role_obj.permissions.all().values('id')
        has_user_permissions_dict = {item['id']: None for item in has_user_permissions}
    elif user_id:
        has_user_permissions = user_obj.roles.filter(permissions__isnull=False).values('permissions').distinct()
        # 获取当前用户所拥有的权限
        # 获取当前页用户拥有的权限的id
        has_user_permissions = user_obj.roles.filter(permissions__isnull=False).values('permissions').distinct()
        has_user_permissions_dict = {item['permissions'] for item in has_user_permissions}
    else:
        has_user_permissions_dict = {}
    # 根据权限id 在模板中做判断，默认选中

    # 获取 用户、角色列表
    all_user_list = models.UserInfo.objects.all()
    all_role_list = models.Role.objects.all()

    # 指定权限菜单的数据结构(一、二、三级别)
    # 所有一级菜单
    menu_permission_list = models.Menu.objects.all().values('id', 'title')
    menu_dict = {}  # 定制一级菜单字典，以一级菜id作为key
    for item in menu_permission_list:
        item['children'] = []
        # 给一级菜单value增加一个children列表，用于存放改菜单所对应的二级菜单信息
        menu_dict[item['id']] = item
        # 同时menu_permission_list 也会相应增加

    # 所有二级菜单
    second_menu_list = models.Permission.objects.filter(menu__isnull=False).values('id', 'title', 'menu_id')
    second_menu_dict = {}
    for row in second_menu_list:
        row['children'] = []
        second_menu_dict[row['id']] = row
        # 根据menu_id 和一级菜单的对应
        menu_dict[row['menu_id']]['children'].append(row)

    # 所有三级菜单（不能做菜单的权限）
    permission_list = models.Permission.objects.filter(menu__isnull=True).values('id', 'title', 'pid')
    for row in permission_list:
        # 根据pid 添加到对应的二级菜单的children列表中
        pid = row['pid']
        if not pid:
            continue
        second_menu_dict[pid]['children'].append(row)

    return render(request, 'rbac/distribute_permissions.html',
                  {'user_list': all_user_list,
                   'role_list': all_role_list,
                   'all_menu_list': menu_permission_list,
                   'second_list': second_menu_list,
                   'user_id': user_id,
                   'has_user_role_dict': has_user_role_dict,
                   'has_user_permissions_dict': has_user_permissions_dict,
                   'role_id': role_id})
